<?php

namespace App\Util;

use App\Core\Exception\DefaultException;
use stdClass;

class ArrayHelper{
    
    /**
     * 合并数组
     * @param array ...$array
     * @return mixed|null
     */
    public static function merge(array ...$array){
        $res = array_shift($array);
        while(!empty($array)){
            foreach(array_shift($array) as $k => $v){
                if(is_int($k)){
                    if(array_key_exists($k, $res)){
                        $res[] = $v;
                    }else{
                        $res[$k] = $v;
                    }
                }elseif(is_array($v) && isset($res[$k]) && is_array($res[$k])){
                    $res[$k] = static::merge($res[$k], $v);
                }else{
                    $res[$k] = $v;
                }
            }
        }
        
        return $res;
    }
    
    /**
     * 读取数组指定键的值
     * @param $array
     * @param $key
     * @param null $default
     * @return mixed|null
     * @throws DefaultException
     * @throws \Throwable
     */
    public static function getValue($array, $key, $default = null){
        if($key instanceof \Closure){
            return $key($array, $default);
        }
        
        if(is_array($key)){
            $lastKey = array_pop($key);
            foreach($key as $keyPart){
                $array = static::getValue($array, $keyPart);
            }
            $key = $lastKey;
        }
        
        if(static::keyExists($key, $array)){
            return $array[$key];
        }
        
        if(($pos = strrpos($key, '.')) !== false){
            $array = static::getValue($array, substr($key, 0, $pos), $default);
            $key = substr($key, $pos + 1);
        }
        
        if(static::keyExists($key, $array)){
            return $array[$key];
        }
        if(is_object($array)){
            // this is expected to fail if the property does not exist, or __get() is not implemented
            // it is not reliably possible to check whether a property is accessible beforehand
            try{
                return $array->$key;
            }catch(\Throwable $e){
                if($array instanceof \ArrayAccess){
                    return $default;
                }
                throw $e;
            }
        }
        
        return $default;
    }
    
    /**
     * 判断Key是否存在
     * @param $key
     * @param $array
     * @param bool $caseSensitive
     * @return bool
     * @throws DefaultException
     */
    public static function keyExists($key, $array, $caseSensitive = true){
        if($caseSensitive){
            // Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case
            // https://secure.php.net/manual/en/function.array-key-exists.php#107786
            if(is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))){
                return true;
            }
            // Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]]
            return $array instanceof \ArrayAccess && $array->offsetExists($key);
        }
        
        if($array instanceof \ArrayAccess){
            throw new DefaultException('Second parameter($array) cannot be ArrayAccess in case insensitive mode');
        }
        
        foreach(array_keys($array) as $k){
            if(strcasecmp($key, $k) === 0){
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 读取二维数组中指定键值合成一维数组
     * @param $array
     * @param $name
     * @param bool $keepKeys
     * @return array
     * @throws DefaultException
     * @throws \Throwable
     */
    public static function getColumn($array, $name, $keepKeys = true){
        $result = [];
        if($keepKeys){
            foreach($array as $k => $element){
                $result[$k] = static::getValue($element, $name);
            }
        }else{
            foreach($array as $element){
                $result[] = static::getValue($element, $name);
            }
        }
        
        return $result;
    }
    
    /**
     * 数组以某键值索引，或以某个键进行分组
     * @param $array
     * @param $key
     * @param array $groups
     * @return array|mixed
     * @throws DefaultException
     * @throws \Throwable
     */
    public static function index($array, $key, $groups = []){
        $result = [];
        $groups = (array)$groups;
        
        foreach($array as $element){
            $lastArray = &$result;
            
            foreach($groups as $group){
                $value = static::getValue($element, $group);
                if(!array_key_exists($value, $lastArray)){
                    $lastArray[$value] = [];
                }
                $lastArray = &$lastArray[$value];
            }
            
            if($key === null){
                if(!empty($groups)){
                    $lastArray[] = $element;
                }
            }else{
                $value = static::getValue($element, $key);
                if($value !== null){
                    if(is_float($value)){
                        $value = str_replace(',', '.', (string)$value);
                    }
                    $lastArray[$value] = $element;
                }
            }
            unset($lastArray);
        }
        
        return $result;
    }
    
    /**
     * 将KV索引数组转换为value,label无索引二维数组
     * @param array $array
     * @return array
     */
    public static function keyArrayToList(array $array){
        $result = [];
        foreach($array as $k => $v){
            $result[] = ['value' => $k, 'label' => $v, 'text' => $v];
        }
        return $result;
    }
    
}